package domain.user;

import java.util.Arrays ;
import java.util.HashSet ;
import java.util.Set ;

import junit.framework.Assert ;
import persistence.CorruptDataException ;
import persistence.Definitions ;
import persistence.PersistenceController ;
import persistence.PersistenceFactory ;
import util.Bug ;
import util.DuplicationException ;
import util.HierarchicalSet ;
import util.NotFoundException ;
import domain.AbstractServerTest ;

/**
 * @author idan
 */
public class UserServerTest extends AbstractServerTest
{
	private static final Privilege[] DEFAULT_PRIVILEGES_AS_ARRAY = { Privilege.VIEW_POST, Privilege.ADD_POST,
		Privilege.REPLY_POST } ;

	private static final Set<Privilege> DEFAULT_PRIVILEGES = new
		HierarchicalSet<Privilege>(Arrays.asList(UserServerTest.DEFAULT_PRIVILEGES_AS_ARRAY)) ;
	private static final String PATH = Definitions.makePath(Definitions.DATA_DIRECTORY, UserServerTest.class) ;
	private static final String EMPTY_MEMBERS_URI = UserServerTest.PATH + "membersEmtpy.xml" ;
	private static final String MEMBERS_URI = UserServerTest.PATH + "members.xml" ;
	private static final Set<Privilege> NO_PRIVILEGES = HierarchicalSet.emptySet() ;

	private static final int AMOUNT_OF_TIMES_TO_TEST_EXISTENCE = 10 ;
	private static final int AMOUNT_OF_ELEMENTS_TO_TEST = 3 ;

	private UserServer server ;
	private UserServer emptyServer ;


	public UserServerTest() throws CorruptDataException
	{
		super() ;
	}

	/**
	 * Test method for {@link domain.user.UserServer#UserServer(PersistenceFactory)}.
	 * Test method for {@link domain.user.UserServer#close()}.
	 */
	public void testConstructorsDestructors()
	{
		Member member = null ;
		try
		{
			member = this.server.createMember("member1", "member1pass", UserServerTest.NO_PRIVILEGES) ;
		}
		catch (final DuplicationException e)
		{
			Assert.fail("incorrect duplication") ;
		}
		this.server.close() ;

		UserServer serverClone = null ;
		try
		{
			serverClone = new UserServer(this.persistenceGenerator) ;
			Assert.assertEquals(member.getUsername(), serverClone.getMember(member.getId()).getUsername()) ;
			Assert.assertEquals(member.getPassword(), serverClone.getMember(member.getId()).getPassword()) ;
			Assert.assertEquals(member.isActive(), serverClone.getMember(member.getId()).isActive()) ;
			Assert.assertEquals(member.getPrivileges(), serverClone.getMember(member.getId()).getPrivileges()) ;
		}
		catch (final CorruptDataException e)
		{
			Assert.fail("database corruption") ;
		}
		catch (final NotFoundException e)
		{
			Assert.fail("elements were not properly saved") ;
		}
		finally
		{
			if (serverClone != null)
			{
				serverClone.close() ;
			}
		}
	}

	/**
	 * Test method for {@link domain.user.UserServer#createMember(java.lang.String, java.lang.String, java.util.Set)}.
	 */
	public void testCreateMember() throws NotFoundException
	{
		try
		{
			final Member member = this.server.createMember("member2", "member2pass", UserServerTest.DEFAULT_PRIVILEGES) ;
			Assert.assertEquals("member2", member.getUsername()) ;
			Assert.assertEquals("member2pass", member.getPassword()) ;
			Assert.assertEquals(UserServerTest.DEFAULT_PRIVILEGES, member.getPrivileges()) ;
			Assert.assertEquals(false, member.isActive()) ;
			Assert.assertSame(member, this.server.getMember(member.getId())) ;
		}
		catch (final DuplicationException e)
		{
			Assert.fail("incorrect duplication") ;
		}
	}

	/**
	 * Test method for {@link domain.user.UserServer#createUser(java.util.Set)}.
	 */
	public void testCreateUser() throws NotFoundException
	{
		final User user = this.server.createUser(UserServerTest.DEFAULT_PRIVILEGES) ;
		Assert.assertEquals(UserServerTest.DEFAULT_PRIVILEGES, user.getPrivileges()) ;
		Assert.assertSame(user, this.server.getUser(user.getId())) ;
	}

	/**
	 * Test method for {@link domain.user.UserServer#deleteUser(long)}.
	 */
	public void testDeleteUser()
	{
		try
		{
			final User user = this.server.createUser(UserServerTest.DEFAULT_PRIVILEGES) ;
			final long id = user.getId() ;
			Assert.assertTrue(this.server.userExists(id)) ;
			this.server.deleteUser(id) ;
			Assert.assertFalse(this.server.userExists(id)) ;
		}
		catch (final NotFoundException e)
		{
			Assert.fail("user not properly created") ;
		}
	}

	/**
	 * Test method for {@link domain.user.UserServer#flush()}.
	 * Test method for {@link domain.user.UserServer#flush(domain.user.Member)}.
	 */
	public void testFlush() throws DuplicationException
	{
		UserServer otherServer = null ;
		try
		{
			final Member member = this.server.createMember("testFlush", "testFlush", UserServerTest.NO_PRIVILEGES) ;
			member.addPrivilege(Privilege.EDIT_ANY_POST) ;
			this.server.flush(member) ;
			otherServer = new UserServer(this.persistenceGenerator) ;
			Assert.assertTrue(this.server.getMember(member.getId()).isAllowed(Privilege.EDIT_ANY_POST)) ;
			otherServer.close() ;

			member.setUsername("flushed-member?") ;
			this.server.flush() ;
			otherServer = new UserServer(this.persistenceGenerator) ;
			Assert.assertEquals(otherServer.getMember(member.getId()).getUsername(), member.getUsername()) ;
			otherServer.close() ;
		}
		catch (final CorruptDataException e)
		{
			if (otherServer != null)
			{
				otherServer.close() ;
			}
			Assert.fail() ;
		}
		catch (final NotFoundException e)
		{
			if (otherServer != null)
			{
				otherServer.close() ;
			}
			Assert.fail() ;
		}
	}

	/**
	 * Test method for {@link domain.user.UserServer#getAllMembers(java.util.Collection)}.
	 */
	public void testGetAllMembersById() throws DuplicationException
	{
		final Set<Member> created = new HashSet<Member>() ;
		final Set<Long> ids = new HashSet<Long>() ;
		for (int i = 0 ; i < UserServerTest.AMOUNT_OF_ELEMENTS_TO_TEST ; i++)
		{
			final Member temp = this.server.createMember("testGetAllMembersCollectionOfLong" + i,
				"member" + i, UserServerTest.DEFAULT_PRIVILEGES) ;
			created.add(temp) ;
			ids.add(temp.getId()) ;
		}

		try
		{
			final Set<Member> obtained = this.server.getAllMembers(ids) ;
			Assert.assertEquals(created, obtained) ;
		}
		catch (final NotFoundException e)
		{
			Assert.fail() ;
		}
	}

	/**
	 * Test method for {@link domain.user.UserServer#getAllUsers(java.util.Collection)}.
	 */
	public void testGetAllUsersById()
	{
		final Set<User> created = new HashSet<User>() ;
		final Set<Long> ids = new HashSet<Long>() ;
		for (int i = 0 ; i < UserServerTest.AMOUNT_OF_ELEMENTS_TO_TEST ; i++)
		{
			final User temp = this.server.createUser(UserServerTest.DEFAULT_PRIVILEGES) ;
			created.add(temp) ;
			ids.add(temp.getId()) ;
		}

		try
		{
			final Set<User> obtained = this.server.getAllUsers(ids) ;
			Assert.assertEquals(created, obtained) ;
		}
		catch (final NotFoundException e)
		{
			Assert.fail() ;
		}
	}

	/**
	 * Test method for {@link domain.user.UserServer#getMember(long)}.
	 */
	public void testGetMemberById() throws DuplicationException
	{
		try
		{
			final Member created = this.server.createMember("testGetMemberById", "testGetMemberById", UserServerTest.DEFAULT_PRIVILEGES) ;
			final Member obtained = this.server.getMember(created.getId()) ;
			Assert.assertSame(created, obtained) ;
		}
		catch (final NotFoundException e)
		{
			Assert.fail() ;
		}

		try
		{
			final Member obtained = this.emptyServer.getMember(30) ;
			Assert.fail("fabricated member: " + obtained) ;
		}
		catch (final NotFoundException e)
		{
			// success
		}
	}

	/**
	 * Test method for {@link domain.user.UserServer#getMember(java.lang.String)}.
	 */
	public void testGetMemberByName() throws DuplicationException
	{
		final Member created = this.server.createMember("testGetMemberByName", "testGetMemberByName",
			UserServerTest.DEFAULT_PRIVILEGES) ;
		final Member obtained = this.server.getMember(created.getUsername()) ;
		Assert.assertSame(created, obtained) ;
		final Member obtainedFromEmpty = this.emptyServer.getMember("fabricated member") ;
		Assert.assertNull(obtainedFromEmpty) ;
	}

	/**
	 * Test method for {@link domain.user.UserServer#getUser(long)}.
	 */
	public void testGetUser() throws DuplicationException
	{
		try
		{
			final User created = this.server.createUser(UserServerTest.DEFAULT_PRIVILEGES) ;
			final User obtained = this.server.getUser(created.getId()) ;
			Assert.assertSame(created, obtained) ;
		}
		catch (final NotFoundException e)
		{
			Assert.fail() ;
		}

		try
		{
			final User obtained = this.emptyServer.getUser(30) ;
			Assert.fail("fabricated user: " + obtained) ;
		}
		catch (final NotFoundException e)
		{
			// success
		}

		User created ;
		try
		{
			created = this.server.createMember("testGetUser", "testGetUser", UserServerTest.DEFAULT_PRIVILEGES) ;
			final User obtained = this.server.getUser(created.getId()) ;
			Assert.assertSame(created, obtained) ;
			Assert.assertTrue(obtained instanceof Member) ;
		}
		catch (final NotFoundException e)
		{
			Assert.fail() ;
		}


	}

	/**
	 * Test method for {@link domain.user.UserServer#userExists(long)}.
	 */
	public void testUserExists() throws NotFoundException
	{
		for (int i = 1 ; i <= UserServerTest.AMOUNT_OF_TIMES_TO_TEST_EXISTENCE ; i++)
		{
			final long id = this.randomGenerator.nextLong() ;
			Assert.assertFalse(this.emptyServer.userExists(id)) ;
		}

		for (int i = 0 ; i < UserServerTest.AMOUNT_OF_TIMES_TO_TEST_EXISTENCE; i++)
		{
			final User user[] = new User[UserServerTest.AMOUNT_OF_ELEMENTS_TO_TEST] ;
			for (int j = 0 ; j < user.length ; j++)
			{
				user[j] = this.server.createUser(UserServerTest.DEFAULT_PRIVILEGES) ;
				Assert.assertTrue(this.server.userExists(user[j].getId())) ;
			}
			for (final User element: user)
			{
				this.server.deleteUser(element.getId()) ;
				Assert.assertFalse(this.server.userExists(element.getId())) ;
			}
		}
	}

	@Override
	protected void clearDatabase()
	{
		try
		{
			PersistenceFactory freshPersistenceBuilder = this
			.newPersistenceGenerator(PersistenceController.BUILD_NEW, false) ;
			freshPersistenceBuilder.newMemberPersistence().close() ;
		}
		catch (CorruptDataException e)
		{
			throw new Bug("report corrupt data when no data expected") ;
		}
	}

	@Override
	protected void setUp() throws Exception
	{
		super.setUp() ;
		this.server = new UserServer(this.persistenceGenerator) ;
		this.emptyServer = new UserServer(this.newPersistenceGenerator(PersistenceController.BUILD_NEW, false)) ;
	}

}
